package java_thinking;

/**
 * 库
 * 编译时java虚拟机根据系统配置的classpath路径(可能有多个)+java.util.Date来查找对应的类
 */
import java.util.Date;

/**
 * @Desc : 示例构造初始化、成员初始化、静态实例初始化、非静态实例初始化，以及他们的顺序
 *         成员的访问权限，
 *         参考：https://blog.csdn.net/Xiao_xuer/article/details/78846177
 *         java类加载时机与过程：http://www.cnblogs.com/javaee6/p/3714716.html?utm_source=tuicool&utm_medium=referral
 *
 *
 *         为什么java类如果没有继承父类，默认就继承了Object类？
 *         了解这个原因其实并不需要知道JVM的实现细节。只要思考一下对于这种虚拟机程序的原理即可。一般对于这种靠虚拟机运行的语言会有两种方法处理默认继承问题。
 *         1、在编译源代码时，当遇到没有父类的类时，编译器会将其指定一个默认的父类(一般为Object)，而虚拟机在处理到这个类时，由于这个类已经有一个默认的父类了，因此，VM仍然会按着常规的方法来处理每一个类。对于这种情况，从编译后的二进制角度来看，所有的类都会有一个父类。
 *         2、编译器仍然按着实际代码进行编译，并不会做额外的处理。如果一个类没有显式地继承于其他的类，编译后的代码仍然没有父类。然后由虚拟机运行二进制代码时，当遇到没有父类的类时，就会自动将这个类看成是Object类的子类(一般这类语言的默认父类都是Object)。
 *         从上面两种情况可以看出，第1种情况是在编译器上做的文章，也就是说，当没有父类时，由编译器在编译时自动为其指定一个父类。第2种情况是在虚拟机上做文章，也就是这个默认的父类是由虚拟机来添加的。
 *
 *         特殊修饰符作用：
 *         1.transient：阻止实例中那些用此关键字声明的变量持久化；当对象被反序列化时(从源文件读取字节序列进行重构)，这样的实例变量值不会被持久化和恢复
 *         2.native：每一个native方法在jvm中都有一个同名的实现体，native方法在逻辑上的判断都是由实现体实现的，另外这种native修饰的方法对返回类型，异常控制等都没有约束
 *
 *         为什么java接口可以多继承，普通类不行：
 *          不允许类多重继承的主要原因是，如果 A 同时继承 B 和 C，而 B 和 C 同时有一个 D 方法，A 如何决定该继承那一个呢？
 *          但接口不存在这样的问题，接口全都是抽象方法继承谁都无所谓，所以接口可以继承多个接口
 * 类编译时生成 InitializeDemo.class文件
 * 类不可以是private的也不可以是protected的
 * OOP具有三大特点：1. 封装性、2. 继承性、3. 多态性
 * 一个类的生命特征包括：加载、验证、准备(所有静态域此时为null或0)、解析、初始化(此时才会初始化静态域的值或引用)、使用、卸载
 */

public class InitializeDemo {
    /************start*************
     * 初始化顺序：1
     * 1.域(成员)初始化
     * 2.静态域(成员)：无论创建多少个对象都只占用一份存储区，
     * 3.静态对象先，非静态对象后
     * 4.基本类型的域会被初始化为0；对象应用会被初始化为null
     * 5.public static int value=123;//在准备阶段value初始值为0 .在初始化阶段才会变为123.
     * 6.静态方法getString()在初始化时不会执行，调用才执行！！！！
     */
    public static String s = "s";
    public static int value=123;
    public String b = "b";
    public String ss = getString();

    public  final String fs1 = new String("fs1");
    public  String getString(){
        // 注意 fs1 为 null
        System.out.println(fs1);
        System.out.println("333");
        return "ss";
    }
    /*end*/

    public static final String fs = new String("fs");

    /************start*************
     * 初始化顺序：2
     * 1.静态实例(静态块)：由多个静态初始化动作组织而成
     * 2.只执行一次
     * 3.这里就不能对b进行引用了，因为静态实例方法是在类被加载到内存时进行,
     *   而b是在创建实例对象时才被初始化引用
     * 4.静态初始化器是对类自身进行初始化；而构造方法是对对象进行初始化。
     * 5.静态初始化器是在类加载入内存的时候，由系统自动调用进行的；
     *   而构造方法是在使用关键字new创建对象时，由系统自动执行，关键字new后面跟的是构造方法
     */
    static {
        System.out.println("static out:"+s);
        System.out.println("static final:" + fs);
    }
    /*end*/


    /************start*************
     * 初始化顺序：3
     * 1.非静态实例:匿名内部类初始化必须执行
     */
    {
        System.out.println("un_static out:"+s);
        System.out.println("un_static out:"+b);
    }
    /*end*/

    /************start*************
     * 初始化顺序：4
     * 构造函数可以直接引用类成员，包括静态域(变量)和非静态域
     * 构造器也是static方法，只是没有显示的写出来，所以，准确的说类是在其任何static成员被访问时加载的
     */
    public InitializeDemo(){
        //不能用实例来访问静态成员 如this.s
        s = "s1";
        this.b = "b1";
        System.out.println("构造函数 out:"+s);
    }
    /*end*/

    /**
     * 不创建带参实例对象则不执行
     * @param s
     * 1.当构造器为私有的情况下，这个类以外是无法直接创建该类，类里面是是可以的，
     * 但是该类的某个static成员是public的话，则其他包下的类仍然是可以调用该static成员！
     * 2.如果package是private，那么同包下的其他类是可以创建该包下的类的，但是别的包下的类创建该
     * 类就不行了
     */
    protected InitializeDemo(String s){
        System.out.println("带参构造函数 out:"+s);
    }
    /*end*/

    public String getValue(){
        System.out.println("实例方法 out:"+ InitializeDemo.s);
        //直接通过类访问，不可以用this访问类成员变量
        InitializeDemo.s = "s2";
        this.b = "111";
        System.out.println("实例方法 out:"+ InitializeDemo.s);
        return InitializeDemo.s ;
    }

    /**
     * 同级包可访问，子类能访问,但子类使用该方法的权限要高于protected
     */
    protected void setValue(){
        System.out.println("this is protect function");
    }

    public static void main(String[] args) {
        InitializeDemo demo = new InitializeDemo();
        demo.getValue();
        String s ;
        Date date = new Date();
//        System.out.println(Test.s);
    }
}

class SunOfInitial extends InitializeDemo{

}

class testClass{
    public static void main(String[] args) {
        InitializeDemo demo = new InitializeDemo();
    }
}